from typing import Optional, Any, Dict

import pytest

from utils.util_logger import logger
from utils.util_setup_teardown import setup_teardown_tool
from utils.util_sql_action import DBUtil
from utils.util_request import UtilRequest
import config
from utils.util_replace_field import replace_field


@pytest.fixture(scope="session", autouse=True)
def setup_and_teardown_session():
    logger.info("测试执行开始")
    yield
    logger.info("测试执行结束")


# 数据库session
@pytest.fixture(scope="session")
def db_util_session():
    db_tool = DBUtil()
    try:
        logger.info(f"Success: 初始化数据库")
    except Exception as e:
        logger.info(f"Fail: 初始化数据库 - str{e}")
    
    yield db_tool

    try:
        db_tool.cursor.close
        db_tool.connection.close
        logger.info(f"Success: 关闭数据库")
    except Exception as e:
        logger.info(f"Fail: 关闭数据库 - str{e}")


# 获取token字符串fixture
@pytest.fixture(scope="session")
def get_token() -> Optional[str]:
    """
    获取认证token的session级fixture
    返回:
        str: 成功返回access_token,失败返回None并记录错误
    """
    auth = UtilRequest()
    try:
        logger.info("读取环境变量...")

        response = auth.send_api(
            url=config.PATH,
            method=config.METHOD,
            data={"username": config.USERNAME,"password": config.PASSWORD}
        )
        response.raise_for_status()  # 自动处理4xx/5xx状态码
        
        token = response.json().get("access_token")
        if not token:
            raise ValueError("Response missing access_token")
            
        logger.info(f"获取token成功，token长度: {len(token)}")  # 安全日志
        return token
        
    except ValueError as e:
        logger.error(f"响应数据异常: {str(e)}")
    except Exception as e:
        logger.critical(f"未知错误: {str(e)}", exc_info=True)
    
    pytest.fail("获取token失败,终止测试会话")  # 关键依赖失败时主动终止
    return None  # 显式返回None（虽然pytest.fail会中断）
        

# 添加认证请求头 {"Authorization": "Bearer {get_token}"}
@pytest.fixture(scope="session")
def has_token_header_api(get_token: str) -> Optional[Any]:
    auth_session = UtilRequest()
    try:
        auth_session.headers.update({"Authorization": f"Bearer {get_token}"})
        logger.info(f'添加请求头成功: {auth_session.headers.get("Authorization")}')
        yield auth_session
    except Exception as e:
        logger.error(f"has_token_header_api-添加请求头失败: {e}")
        raise e
    
    
@pytest.fixture(scope="class")
def api_class_has_content(has_token_header_api):
    # 上下文管理
    TEST_CONTENT = {"test": "cxg"}
    try:
        # 动态添加属性（类型安全方式）
        if not hasattr(has_token_header_api, 'content_class'):
            has_token_header_api.content_class = TEST_CONTENT
            logger.debug(f"has_token_header_api.content_class: {has_token_header_api.content_class}")
        yield has_token_header_api
    except Exception as e:
        logger.error(f"api_class_has_content获取auth_class失败: {str(e)}")
        raise e
    

@pytest.fixture()
def setup_and_teardown(request, db_util_session, api_class_has_content):
    """获取setup、teardown数据信息,使用反射机制读取数据库信息"""
    # 1. 参数校验
    if not hasattr(request, "param"):
        pytest.skip(reason="未提供测试用例,无需处理setup-teardown")

    test_data: Dict[str, Any] = request.param
    setup_data: Optional[str] = test_data.get("setup")
    teardown_data: Optional[str] = test_data.get("teardown")

    # 2. 处理setup
    try:
        if setup_data:
            setup_teardown_tool(db_util_session, replace_field(setup_data, api_class_has_content), "setup")
            logger.info(f"setup Success: {setup_data}")
            # logger.info(f"has_token_header_api: {has_token_header_api.}")
        else:
            logger.info(f"Do not need Setup!")
    except Exception as e:
        logger.info(f"setup Fail: {str(e)}")
        pytest.fail(reason=f'{test_data.get("case_name")}')

    yield test_data

    # 3 处理teardown
    try:
        if teardown_data:
            setup_teardown_tool(db_util_session, replace_field(teardown_data, api_class_has_content), "teardown")
            logger.info(f"teardown Success: {teardown_data}")
        else:
            logger.info(f"Do not need Teardown!")
    except Exception as e:
        logger.info(f"teardown Fail: {str(e)}")
        pytest.fail(reason=f'{test_data.get("case_name")}')
